
# python imports
import logging

# appengine imports
from google.appengine.ext import db
from google.appengine.api import memcache
from google.appengine.datastore import entity_pb

class InternalError(Exception):
	pass

class State(db.Model):
	# properties
	name = db.StringProperty(indexed=True)
	value = db.IntegerProperty()

class StateStore:
	# constructor
	def __init__(self,key,max_entities=10):
		self.key = key
		self.max_entities = max_entities
	# static methods
	@classmethod
	def serialize(cls,models):
		if models is None:
			return None
		elif isinstance(models, db.Model):
			# Just one instance
			return db.model_to_protobuf(models).Encode()
		elif isinstance(models,list):
			# A list
			return [db.model_to_protobuf(x).Encode() for x in models]
		else:
			raise InternalError("serialize: invalid data type")
	@classmethod
	def deserialize(cls,data):
		if data is None:
			return None
		elif isinstance(data, str):
			# Just one instance
			return db.model_from_protobuf(entity_pb.EntityProto(data))
		elif isinstance(data,list):
			# Multiple instances
			return [db.model_from_protobuf(entity_pb.EntityProto(x)) for x in data]
		else:
			raise InternalError("deserialize: invalid data type")
	def _entities(self):
		entities = self.deserialize(memcache.get(self.key))
		if entities is None:
			logging.debug("Unable to deserialize entities from memcache, fetching from storage")
			entities = State.all().fetch(self.max_entities)
			memcache.set(self.key,self.serialize(entities))
		return entities
	def keys(self):
		entities = self._entities()
		return [ entity.name for entity in entities ]
	def reset(self):
		entities = State.all()
		for entity in entities:
			entity.delete()
		memcache.delete(self.key)
	def __getitem__(self,name):
		assert isinstance(name,basestring)
		entities = self._entities()
		if entities is None:
			logging.error("No entities, returning None")
			return None
		for entity in entities:
			if entity.name==name:
				return entity.value
		logging.warn("No entity for %s: returning None" % name)
		return None
	def __setitem__(self,name,value):
		assert isinstance(name,basestring)
		assert isinstance(value,int)
		# remove existing entities with same name
		entities = State.all()
		for entity in entities:
			if entity.name==name:
				entity.delete()
		# add entity back in
		entity = State()
		entity.name = name
		entity.value = value
		entity.put()
		# delete the memcache
		memcache.delete(self.key)
